#include <linux/version.h>/* LINUX_VERSION_CODE KERNEL_VERSION()*/
#include <linux/init.h>   /* module_init module_exit */
#include <linux/kernel.h>
#include <linux/module.h> /* MODULE_LICENSE */

#include <linux/of.h>
#include <linux/slab.h>

#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/notifier.h>
#include <linux/utsname.h>     // （获取内核信息）
#include <generated/compile.h> // 编译用户

#include <linux/fs.h>
#include <linux/uaccess.h>

#include "./timestamp_autogenerated.h"
#include "./version_autogenerated.h"
#include "./sdrv_common.h"

// ==================== version ====================
// 版本信息组合
#define DRIVER_MAJOR   1
#define DRIVER_MINOR   0
#define DRIVER_PATCH   0

#define _STR(x)           #x
#define STR(x)            _STR(x)
#define VERSION_STR       STR(DRIVER_MAJOR) "." STR(DRIVER_MINOR) "." STR(DRIVER_PATCH)
#define FULL_VERSION_INFO VERSION_STR "@" KERNEL_KO_VERSION "@" KERNEL_KO_DATE " " KERNEL_KO_TIME

// ==================== debug PRT/PRTE ====================
// #undef PRT
// #undef PRTE
#ifndef PRT
#define PRT printk
#endif
#ifndef PRTE
#define PRTE printk
#endif

// ==================== debug 调试的一些变量和宏定义 ====================
#define PROC_DIR  "sdebug"   // /proc/PROC_DIR
#define PROC_FILE "revision" // /proc/PROC_DIR/revision

// 定义驱动版本信息结构体
struct driver_info{
    char name[MODULE_NAME_LEN];
    char version[64];
    struct list_head list;
};

static LIST_HEAD(driver_list);
static DEFINE_MUTEX(driver_list_mutex);
static struct proc_dir_entry *proc_dir = NULL;
static struct proc_dir_entry *proc_entry = NULL;

// ==================== uboot的版本信息 ====================
// 需要修改设uboot源码，将uboot的版本信息传入linux设备树的 /chosen 节点
struct uboot_version {
    const char *plain_version;
    const char *git_hash;
    const char *build_time;
    const char *build_user;
};

static struct uboot_version uboot_ver = {0};

/**
 * 解析设备树中uboot版本节点:
 *    \/ {
 *        chosen {
 *            // ......
 *            u-boot-version = "U-Boot 2019.04-g9c2e18c5-dirty";
 *            u-boot-build-time = "Apr 18 2025 00:12:11 +0800";
 *            u-boot-build-user = "sumu";
 *        };
 *    };
 */
static int parse_uboot_version(void)
{
    struct device_node *np = of_find_node_by_path("/chosen");
    if (!np)
    {
        PRTE("chosen node not found!\n");
        goto no_version_node;
    }
    // 1. git版本号 U-Boot 2019.04-g9c2e18c5-dirty
    if (of_property_read_string(np, "u-boot-version", &uboot_ver.plain_version))
    {
        uboot_ver.plain_version = "unknown";
    }

    // 2. 编译日期和时间 Apr 18 2025 00:12:11 +0800
    if (of_property_read_string(np, "u-boot-build-time", &uboot_ver.build_time))
    {
        uboot_ver.build_time = "unknown";
    }

    // 3. 编译主机用户名 sumu
    if (of_property_read_string(np, "u-boot-build-user", &uboot_ver.build_user))
    {
        uboot_ver.build_time = "unknown";
    }
    of_node_put(np);

    return 0;
no_version_node:
    uboot_ver.plain_version = "unknown";
    uboot_ver.build_time = "unknown";
    uboot_ver.build_user = "unknown";
    return -ENODEV;
}

static void uboot_info_show(struct seq_file *m)
{
    char *git_hash_pos = NULL;

    parse_uboot_version();//获取设备树的节点信息

    seq_puts(m, "----- U-Boot Version -----\n");
    seq_printf(m, "%-20s\n", "uboot info");

    // 1.uboot版本 uboot_ver.plain_version = U-Boot 2019.04-ge30c4250-dirty
    seq_printf(m, "\t%-20s: %s\n", "U-Boot Version", uboot_ver.plain_version);

    // 2. 编译时间 uboot_ver.build_time = Apr 12 2025 15:09:19 +0000
    seq_printf(m, "\t%-20s: %s\n", "Build Time", uboot_ver.build_time);

    // 3. 编译用户名
    seq_printf(m, "\t%-20s: %s\n", "Build User", uboot_ver.build_user);

    // 4. Git哈希 git_hash_pos = U-Boot 2019.04-ge30c4250-dirty 中的-g出现的位置
    git_hash_pos = strstr(uboot_ver.plain_version, "-g");
    if (git_hash_pos && strlen(git_hash_pos) > 1) 
    {
        git_hash_pos++;//跳过-
        //char git_ver[16] = {0};
        //snprintf(kernel_info, sizeof(kernel_info), "%s@%s", uts->release, git_ver);
        seq_printf(m, "\t%-20s: %s\n", "Git Commit", git_hash_pos);
    } 
    else
    {
        seq_printf(m, "\t%-20s: %s\n", "Git Commit", "Not enabled (CONFIG_LOCALVERSION_AUTO=n)");
    }

    seq_puts(m, "\n");
    return;
}

// ==================== kernel的版本信息 ====================
static void kernel_info_show(struct seq_file *m)
{
    char *git_hash_pos = NULL;
    struct new_utsname *uts = NULL; // 获取内核信息

    uts = utsname();// 获取版本信息

    seq_puts(m, "----- kernel Version -----\n");
    seq_printf(m, "%-20s\n", "kernel info");

    // 1.内核版本 uts->release = 4.19.71-00011-g8e83579a5-dirty
    seq_printf(m, "\t%-20s: %s\n", "Kernel Version", uts->release);

    // 2. 编译时间 uts->version = #2 SMP Sat Apr 12 23:42:27 CST 2025
    seq_printf(m, "\t%-20s: %s\n", "Build Time", uts->version);

    // 3. 编译用户 LINUX_COMPILE_BY = sumu,这个不一定准，这个主要还是取决于编译这个驱动所使用的内核是在哪里编译的
    seq_printf(m, "\t%-20s: %s\n", "Build User", LINUX_COMPILE_BY);

    // 4. Git哈希 git_hash_pos = 4.19.71-00011-g8e83579a5-dirty 中的-g出现的位置
    git_hash_pos = strstr(uts->release, "-g");
    if (git_hash_pos && strlen(git_hash_pos) > 1) 
    {
        git_hash_pos++;//跳过-
        //char git_ver[16] = {0};
        //snprintf(kernel_info, sizeof(kernel_info), "%s@%s", uts->release, git_ver);
        seq_printf(m, "\t%-20s: %s\n", "Git Commit", git_hash_pos);
    } 
    else
    {
        seq_printf(m, "\t%-20s: %s\n", "Git Commit", "Not enabled (CONFIG_LOCALVERSION_AUTO=n)");
    }

    seq_puts(m, "\n");
    return;
}

// ==================== 设备树的版本信息 ====================
// 这个需要将版本信息写入到设备树中
struct dtb_version {
    const char *git_hash;
    const char *kernel_ver;
    const char *version;
    const char *build_time;
    const char *build_user;
};

static struct dtb_version dtb_ver = {0};

/**
 * 解析设备树版本节点:
 *    \/ {
 *        dtb_ver {
 *            compatible = "dtb-ver";
 *            kernel-ver = "4.19.71-g186fec3(prj)";
 *            dtb-ver = "1.0.1";
 *            dtb-build-user = "sumu";
 *            dtb-git-hash = "g186fec3";
 *            dtb-build-time = "Fri Apr 18 15:26:25 CST 2025";
 *        };
 *    };
 */
static int parse_dt_version(void)
{
    struct device_node *np = of_find_node_by_path("/dtb_ver");
    if (!np)
    {
        PRTE("dtb_version_info node not found!\n");
        goto no_version_node;
    }
    // 1. git版本号 g186fec3/unknown
    if (of_property_read_string(np, "dtb-git-hash", &dtb_ver.git_hash))
    {
        dtb_ver.git_hash = "unknown";
    }
    // 2. 内核版本号 4.19.71-g186fec3(prj) 带prj的是在内核外编译的，后面的git版本号是仓库的版本号
    //    不带prj的是在内核中编译的，后面的版本号是kernel源码目录仓库的版本号
    if (of_property_read_string(np, "kernel-ver", &dtb_ver.kernel_ver))
    {
        dtb_ver.kernel_ver = "unknown";
    }
    // 3. 设备树版本 1.0.1
    if (of_property_read_string(np, "dtb-ver", &dtb_ver.version))
    {
        dtb_ver.version = "unknown";
    }
    // 4. 编译时间 Fri Apr 18 15:26:25 CST 2025
    if (of_property_read_string(np, "dtb-build-time", &dtb_ver.build_time))
    {
        dtb_ver.build_time = "unknown";
    }

    // 5. 编译用户 sumu
    if (of_property_read_string(np, "dtb-build-user", &dtb_ver.build_user))
    {
        dtb_ver.build_user = "unknown";
    }
    of_node_put(np);

    return 0;
no_version_node:
    dtb_ver.git_hash = "unknown";
    dtb_ver.kernel_ver = "unknown";
    dtb_ver.version = "unknown";
    dtb_ver.build_time = "unknown";
    dtb_ver.build_user = "unknown";
    return -ENODEV;
}

static void devicetree_info_show(struct seq_file *m)
{
    parse_dt_version();//获取设备树的节点信息

    seq_puts(m, "-----  dts Version   -----\n");
    seq_printf(m, "%-20s\n", "devicetree info");

    // 1. 内核版本号 4.19.71-g186fec3(prj) 带prj的是在内核外编译的，后面的git版本号是仓库的版本号
    //    不带prj的是在内核中编译的，后面的版本号是kernel源码目录仓库的版本号
    seq_printf(m, "\t%-20s: %s\n", "DTB Kernel Version", dtb_ver.kernel_ver);
    // 2. 设备树版本 dtb_ver.version = 1.0.1
    seq_printf(m, "\t%-20s: %s\n", "DTB Version", dtb_ver.version);
    // 3. 编译时间 Fri Apr 18 15:26:25 CST 2025
    seq_printf(m, "\t%-20s: %s\n", "Build Time", dtb_ver.build_time);
    // 4. 编译用户 sumu
    seq_printf(m, "\t%-20s: %s\n", "Build User", dtb_ver.build_user);
    // 5. Git哈希 g186fec3/unknown
    seq_printf(m, "\t%-20s: %s\n", "Git Commit", dtb_ver.git_hash);

    seq_puts(m, "\n");

    return;
}
// ==================== rootfs 的版本信息 ====================
/**
 * 读取 /version.txt文件，文件内容示例：
 *      Buildrootfs:buildroot-2023.05.1
 *      Build Time:Jan 01 1970 06:42:44 +0000
 *      Build User:sumum
 *      Git Commit:d7c825a
 * 注意：用户空间的`version.txt`文件需要在内核模块中被读取。由于内核模块不能直接使用用户空间的库函数（如fopen、fread），
 *      需要使用内核提供的文件操作函数，比如`filp_open`、`kernel_read`、`filp_close`。
 */
#define VERSION_FILE  "/version.txt"
#define MAX_FIELD_LEN (64)
// 版本信息结构体
typedef struct rootfs_version_info {
    char rootfs[MAX_FIELD_LEN];
    char time[MAX_FIELD_LEN];
    char user[MAX_FIELD_LEN];
    char commit[MAX_FIELD_LEN];
}rootfs_version_info_t;

static rootfs_version_info_t rootfs_ver = {0};

// 内核字符串处理函数, include/linux/string.h 中有
/*static inline char *strim(char *str)
{
    char *end;
    while (*str && (*str == ' ' || *str == '\t'))
    {
        str++;
    }
    end = str + strlen(str) - 1;
    while (end > str && (*end == ' ' || *end == '\t' || *end == '\n'))
    {
        end--;
    }
    *(end + 1) = '\0';
    return str;
}*/

/* 读取版本文件内容到内存 */
static int read_version_file(void)
{
    ssize_t ret = 0;
    struct file *filp;
    loff_t pos = 0;
    mm_segment_t old_fs = 0;
    
    char *file_content = NULL;
    size_t file_size = 0;
    char *line = NULL;
    char *key = NULL;
    char *value = NULL;

    // 打开文件
    filp = filp_open(VERSION_FILE, O_RDONLY, 0);
    if (IS_ERR(filp))
    {
        pr_err("Cannot open %s\n", VERSION_FILE);
        return PTR_ERR(filp);
    }

    // 获取文件大小
    file_size = i_size_read(file_inode(filp));
    if (file_size == 0)
    {
        pr_err("Empty version file\n");
        filp_close(filp, NULL);
        return -EINVAL;
    }

    // 分配内存
    file_content = kzalloc(file_size + 1, GFP_KERNEL);
    if (!file_content)
    {
        filp_close(filp, NULL);
        return -ENOMEM;
    }

    // 切换到内核地址空间
    old_fs = get_fs();
    set_fs(KERNEL_DS);

    // 读取文件内容
    ret = kernel_read(filp, file_content, file_size, &pos);
    if (ret != file_size)
    {
        pr_err("Read failed: %zd\n", ret);
        kfree(file_content);
        file_content = NULL;
        ret = -EIO;
    }
    else
    {
        file_content[file_size] = '\0'; // 确保字符串终止
        ret = 0;
    }
    // 解析文件内容
    // 逐行解析
    while ((line = strsep(&file_content, "\n")) != NULL)
    {
        if (!*line)
        {
            continue;
        }

        key = strim(strsep(&line, ":"));
        if (!line)
        {
            continue;
        }
        value = strim(line);

        if (!strcmp(key, "Buildrootfs"))
        {
            strlcpy(rootfs_ver.rootfs, value, MAX_FIELD_LEN);
        }
        else if (!strcmp(key, "Build Time"))
        {
            strlcpy(rootfs_ver.time, value, MAX_FIELD_LEN);
        }
        else if (!strcmp(key, "Build User"))
        {
            strlcpy(rootfs_ver.user, value, MAX_FIELD_LEN);
        }
        else if (!strcmp(key, "Git Commit"))
        {
            strlcpy(rootfs_ver.commit, value, MAX_FIELD_LEN);
        }
    }
    // 恢复地址空间并关闭文件
    set_fs(old_fs);
    filp_close(filp, NULL);
    kfree(file_content);
    return ret;
}

static void rootfs_info_show(struct seq_file *m)
{
    seq_puts(m, "-----  rootfs Version   -----\n");
    seq_printf(m, "%-20s\n", "rootfs info");
    read_version_file();
    seq_printf(m, "\t%-20s: %s\n", "Buildrootfs", rootfs_ver.rootfs);
    seq_printf(m, "\t%-20s: %s\n", "Build Time", rootfs_ver.time);
    seq_printf(m, "\t%-20s: %s\n", "Build User", rootfs_ver.user);
    seq_printf(m, "\t%-20s: %s\n", "Git Commit", rootfs_ver.commit);
    seq_puts(m, "\n");
    return;
}
// ==================== 模块自己的版本信息 ====================
// 手动添加当前模块的信息到链表
static void add_self_to_list(void)
{
    struct module *mod = THIS_MODULE; // 获取当前模块的指针
    struct driver_info *drv = NULL;

    drv = kmalloc(sizeof(struct driver_info), GFP_KERNEL);
    if (!drv)
        return;

    strscpy(drv->name, mod->name, MODULE_NAME_LEN);
    /* 版本获取方式与其他模块一致 */
    snprintf(drv->version, sizeof(drv->version), "%s", mod->version ?: "Unversioned");

    mutex_lock(&driver_list_mutex);
    list_add_tail(&drv->list, &driver_list);
    mutex_unlock(&driver_list_mutex);
}

// ==================== /proc/PROC_DIR/PROC_FILE ops ====================
static int revision_show(struct seq_file *m, void *v)
{
    struct driver_info *info = NULL;

    mutex_lock(&driver_list_mutex);
    // 1. uboot
    uboot_info_show(m);

    // 2. kernel的信息
    kernel_info_show(m);
    
    // 3. 设备树的信息
    devicetree_info_show(m);

    // 4. rootfs
    rootfs_info_show(m);

    // 5. 驱动的信息
    seq_puts(m, "----- driver Version -----\n");
    list_for_each_entry(info, &driver_list, list)
    {
        seq_printf(m, "%-20s %s\n", info->name, info->version);
    }
    mutex_unlock(&driver_list_mutex);
    return 0;
}

static int revision_open(struct inode *inode, struct file *file)
{
    return single_open(file, revision_show, NULL);
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
static const struct proc_ops g_proc_fops = {
    .proc_open    = revision_open,
    .proc_read    = seq_read,
    .proc_lseek   = seq_lseek,
    .proc_release = single_release,
};
#else
static const struct file_operations g_proc_fops = {
    .owner   = THIS_MODULE,
    .open    = revision_open,
    .read    = seq_read,
    .llseek  = seq_lseek,
    .release = single_release,
};
#endif

// ==================== sdriver_revision 驱动实现 ====================
// 模块通知链回调
static int module_notifier_cb(struct notifier_block *nb, unsigned long action, void *data)
{
    struct module *mod = data;
    struct driver_info *info = NULL;

    mutex_lock(&driver_list_mutex);

    switch (action)
    {
		case MODULE_STATE_COMING: // 模块加载
			info = kmalloc(sizeof(*info), GFP_KERNEL);
			if (!info)
				break;

			strscpy(info->name, mod->name, MODULE_NAME_LEN);
			strscpy(info->version, mod->version ?: "Unversioned", sizeof(info->version));
			list_add_tail(&info->list, &driver_list);
			break;

		case MODULE_STATE_GOING: // 模块卸载
		{
			struct driver_info *tmp;
			list_for_each_entry(tmp, &driver_list, list)
			{
				if (strcmp(tmp->name, mod->name) == 0)
				{
					list_del(&tmp->list);
					kfree(tmp);
					break;
				}
			}
			break;
		}
    }

    mutex_unlock(&driver_list_mutex);
    return NOTIFY_OK;
}

// 注册模块通知链
static struct notifier_block module_nb = {
    .notifier_call = module_notifier_cb,
    .priority = INT_MAX,
};

/**
 * @brief  sdrv_driver_init()
 * @note   注册我们的驱动
 * @param  [in]
 * @param  [out]
 * @retval 
 */
static __init int sdrv_driver_init(void)
{
    int ret = 0;
	/*
    printk("*** [%s:%d]Build Time: %s %s, git version:%s LINUX_VERSION=%d.%d.%d ***\n", __FUNCTION__,
           __LINE__, KERNEL_KO_DATE, KERNEL_KO_TIME, KERNEL_KO_VERSION, 
           (LINUX_VERSION_CODE >> 16) & 0xff, (LINUX_VERSION_CODE >> 8) & 0xff, LINUX_VERSION_CODE & 0xff);
    */
    //创建 /proc/PROC_DIR 目录
    proc_dir = proc_mkdir(PROC_DIR, NULL);
    if (!proc_dir)
	{
		PRTE("proc_mkdir %s fail\n", PROC_DIR);
		goto err_proc_mkdir;
	}

	//创建 /proc/PROC_DIR/PROC_FILE 文件
    proc_entry = proc_create(PROC_FILE, 0444, proc_dir, &g_proc_fops);
    if (!proc_entry)
    {
        PRTE("proc_create %s fail!\n", PROC_FILE);
        goto err_proc_create;
    }

    ret = register_module_notifier(&module_nb);
    if (ret) 
    {
        PRT("Failed to register_module_notifier!ret=%d\n", ret);
        goto err_register_module_notifier;
    }

    /* 手动添加当前模块信息 */
    add_self_to_list();

    //PRT("sdrv_driver module init success!\n");
	return 0;
err_register_module_notifier:
	proc_remove(proc_entry);
err_proc_create:
	proc_remove(proc_dir);
err_proc_mkdir:
    return ret;
}

/**
 * @brief  sdrv_driver_exit
 * @note   注销驱动
 * @param  [in]
 * @param  [out]
 * @retval 
 */
static __exit void sdrv_driver_exit(void)
{
    struct driver_info *info, *tmp;

    unregister_module_notifier(&module_nb);
    list_for_each_entry_safe(info, tmp, &driver_list, list)
    {
        list_del(&info->list);
        kfree(info);
    }

    proc_remove(proc_entry);
    proc_remove(proc_dir);
}

module_init(sdrv_driver_init); // 将__init定义的函数指定为驱动的入口函数
module_exit(sdrv_driver_exit); // 将__exit定义的函数指定为驱动的出口函数

/* 模块信息(通过 modinfo xxx.ko 查看) */
MODULE_VERSION(FULL_VERSION_INFO);                                   /* 定义版本 */
MODULE_LICENSE("GPL v2");                                            /* 源码的许可证协议 */
MODULE_AUTHOR("sumu");                                               /* 字符串常量内容为模块作者说明 */
MODULE_DESCRIPTION("Track Driver Versions in /proc/debug/revision"); /* 字符串常量内容为模块功能说明 */
MODULE_ALIAS("module's other name");                                 /* 字符串常量内容为模块别名 */
